Use the Select URL API to take advantage of Shared Storage to determine what creative a user sees across sites.
An advertiser or a content producer may want to apply different content rotation strategies to a campaign, and rotate the contents or creatives to increase effectiveness. The Select URL API can be used to run different rotation strategies, such as sequential rotation and evenly-distributed rotation, across different sites.
With Select URL API creative rotation, you can store data, such as creative ID, view counts, and user interaction to determine which creative users see across different sites.
For more information on the underlying API and how selection works, explore the Select URL API documentation.
Try creative rotation
To experiment with creative rotation, make sure Select URL API and Shared Storage are enabled:
- In
chrome://settings/content/siteData
, selectAllow sites to save data on your device
orDelete data sites have saved to your device when you close all windows
. - In
chrome://settings/adPrivacy/sites
, selectSite-suggested ads
.
Try our Shared Storage live demo for a live version of the code samples in this document.
Demo with code samples
In this example:
creative-rotation.js
is the third-party script defining the content to be rotated, along with any data that determines the next content to select and display, such as weights in this example. The publisher page executes this script. This script calls shared storage worklet to determine which content to display based on available data in storage and the list of URLs to select from.creative-rotation-worklet.js
is the third-party's shared storage worklet that looks up the rotation strategy, calculates what content to publish next, and returns that piece of content.
How the demo works
- When a user visits the publisher page, the page loads the third-party's
creative-rotation.js
script. The creative rotation script is responsible for loading and running the shared storage worklet. The script supplies the worklet call with a list of URLs to select from. - In the worklet, if shared storage hasn't been initialized yet, the storage is initialized with the initial creative rotation strategy and creative index. The initial rotation strategy used in this demo is sequential strategy.
- The worklet reads the rotation mode from shared storage and returns the index of the next ad. In the case of sequential rotation mode, it also updates the creative index in shared storage with the new value to be used for the next call.The worklet returns a
FencedFrameConfig
or opaque URN object based on theresolveToConfig
value used when callingselectURL
. - The creative-rotation script displays the selected ad in a Fenced Frame or an iframe. See the render an ad document for details on return types.
- When a user changes the rotation mode, the shared storage worklet updates the creative rotation mode value stored in shared storage.
- When reloading the publisher page, steps 1-4 are repeated enabling the selection of the next ad to be viewed based on the selected rotation strategy
Code samples
The following are the code samples for creative-rotation.js and creative-rotation-worklet.js.
const contentProducerUrl = 'https://your-server.example';
// Ad config with the URL of the ad, a probability weight for rotation, and the clickthrough rate.
const DEMO_AD_CONFIG = [
{
url: `${contentProducerUrl}/ads/ad-1.html`,
weight: 0.7,
},
{
url: `${contentProducerUrl}/ads/ad-2.html`,
weight: 0.2,
},
{
url: `${contentProducerUrl}/ads/ad-3.html`,
weight: 0.1,
},
];
async function setRotationMode(rotationMode) {
// Load the worklet module
const creativeRotationWorklet = await window.sharedStorage.createWorklet(
`${contentProducerUrl}/url-selection/creative-rotation-worklet.js`,
{ dataOrigin: 'script-origin' }
);
await creativeRotationWorklet.run('set-rotation-mode', {
data: { rotationMode }
});
console.log(`creative rotation mode set to ${rotationMode}`);
}
async function injectAd() {
// Load the worklet module
const creativeRotationWorklet = await window.sharedStorage.createWorklet(
`${contentProducerUrl}/url-selection/creative-rotation-worklet.js`,
{ dataOrigin: 'script-origin' }
);
const urls = DEMO_AD_CONFIG.map(({ url }) => ({ url }));
// Resolve the selectURL call to a fenced frame config only when it exists on the page
const resolveToConfig = typeof window.FencedFrameConfig !== 'undefined';
// Run the URL selection operation to determine the next ad that should be rendered
const selectedUrl = await creativeRotationWorklet.selectURL('creative-rotation', urls, {
data: DEMO_AD_CONFIG,
resolveToConfig
});
const adSlot = document.getElementById('ad-slot');
if (resolveToConfig && selectedUrl instanceof FencedFrameConfig) {
adSlot.config = selectedUrl;
} else {
adSlot.src = selectedUrl;
}
}
injectAd();
class SelectURLOperation {
async run(urls, data) {
// Initially set the storage to sequential mode for the demo
await SelectURLOperation.seedStorage();
// Read the rotation mode from Shared Storage
const rotationMode = await sharedStorage.get('creative-rotation-mode');
// Generate a random number to be used for rotation
const randomNumber = Math.random();
let index;
switch (rotationMode) {
/**
* Sequential rotation
* - Rotates the creatives in order
* - Example: A -> B -> C -> A ...
*/
case 'sequential':
const currentIndex = await sharedStorage.get('creative-rotation-index');
index = parseInt(currentIndex, 10);
const nextIndex = (index + 1) % urls.length;
console.log(`index = ${index} / next index = ${nextIndex}`);
await sharedStorage.set('creative-rotation-index', nextIndex.toString());
break;
/**
* Evenly-distributed rotation
* - Rotates the creatives with equal probability
* - Example: A=33% / B=33% / C=33%
*/
case 'even-distribution':
index = Math.floor(randomNumber * urls.length);
break;
/**
* Weighted rotation
* - Rotates the creatives with weighted probability
* - Example: A=70% / B=20% / C=10%
*/
case 'weighted-distribution':
console.log('data = ', JSON.stringify(data));
// Find the first URL where the cumnulative sum of the weights
// exceed the random number. The array is sorted by the weight
// in descending order.
let weightSum = 0;
const { url } = data
.sort((a, b) => b.weight - a.weight)
.find(({ weight }) => {
weightSum += weight;
return weightSum > randomNumber;
});
index = urls.indexOf(url);
break;
default:
index = 0;
}
console.log(JSON.stringify({ index, randomNumber, rotationMode }));
return index;
}
// Set the mode to sequential and set the starting index to 0.
static async seedStorage() {
await sharedStorage.set('creative-rotation-mode', 'sequential', {
ignoreIfPresent: true,
});
await sharedStorage.set('creative-rotation-index', 0, {
ignoreIfPresent: true,
});
}
}
class SetRotationModeOperation {
async run({ rotationMode }) {
await sharedStorage.set('creative-rotation-mode', rotationMode);
}
}
// Register the operation as 'creative-rotation'
register('creative-rotation', SelectURLOperation);
register('set-rotation-mode', SetRotationModeOperation);
Walkthrough with screenshots
To access the Creatives Rotation using Select URL API and Shared storage, go to https://shared-storage-demo.web.app/. Choose 'Creative Rotation' demo.
Choose to explore the demo as 'Publisher A'. You will be redirected to https://shared-storage-demo-publisher-a.web.app/creative-rotation. The page loads numbered content based on the creative rotation data saved in Shared Storage, accessed through the Select URL API. The demo modes for creative rotation are sequential, even distribution and weighted distribution. The worklet executes the logic to select the content that appears in the iframe. The following image shows the publisher page.
A screenshot shows the Publisher A page with an image of the number 1 and controls to choose creative rotation strategies. To view what is stored in Shared Storage, navigate to Application -> Shared Storage in Chrome DevTools. Two entries are created for shared storage. An empty storage is available for the https://shared-storage-demo-publisher-a.web.app origin. This will contain storage specific to that origin and will remain empty for our demo since the publisher does not need to write to shared storage. Note that a similar storage will be created for Publisher B when you visit that page at a later time for the demo.
Chrome DevTools shows empty Shared Storage for Publisher A. Another Shared Storage entry will be created for the https://shared-storage-demo-content-producer.web.app origin. This is the storage of the third-party iframe embedded on the publisher page. This storage will be used to share data between the two available publishers to coordinate creative selection. This shared storage will be used to save information about the shown creative and rotation strategy by saving two key-value pairs. The first key used in the demo is
creative-rotation-index
whose value is the current creative index in sequential mode. The second key iscreative-rotation-mode
which dictates the rotation strategy used.A screenshot shows the Chrome DevTools shared storage with two key-value pairs: creative-rotation-index: 1 and creative-rotation-mode: "sequential." Refreshing the page while in sequential mode will result in showing the next creative in the sequence 1, 2, 3, 1, …, The value for the key creative-rotation-index will change according to the index of the shown creative while in sequential mode.
A screenshot shows Publisher A's webpage and DevTools. The creative shown is 2, creative-rotation-mode is sequential, and creative-rotation-index is 2. Changing the creative rotation mode using the control buttons will update the value for key creative-rotation-mode into the corresponding strategy. This will be used by the worklet code to choose the next creative to be shown in the iframe. Note that the value saved for creative-rotation-index doesn"t change for rotation modes other than sequential.
A screenshot shows Publisher A's webpage and DevTools. The creative shown is 1, creative-rotation-mode is weighted distribution, and creative-rotation-index is 2 (unused). Navigate to the page for "Publisher B" at https://shared-storage-demo-publisher-b.web.app/creative-rotation. In sequential mode the creative shown should be the next creative in the sequence shown when visiting the URL for "Publisher A". Inspecting the Shared Storage for the content producer, you can find that both "Publisher A" and "Publisher B" share the same storage and are using the settings there to select the next creative to show and the rotation strategy to use.
Publisher B's webpage and DevTools. The Shared Storage creative is 3, the creative-rotation index is 3, and the creative-rotation-mode is sequential. The Shared Storage for "Publisher B" is empty, similar to "Publisher A"'s Shared Storage.
Chrome DevTools showing empty Shared Storage for Publisher B origin.
Use cases
All available use cases for Select URL API can be found in this section. We'll continue to add examples as we receive feedback and discover new test cases.
- Rotate ad creatives: Store data, such as creative ID and user interaction, to determine which creative users' see across different sites.
- Select ad creatives by frequency: Use view count data to determine which creative users' see across different sites.
- Run A/B testing: You can assign a user to an experiment group, then store that group in Shared Storage to be accessed cross-site.
- Customize experience for known customers: Share custom content and calls-to-action based on a user's registration status or other user states.
Engage and share feedback
Note that the Select URL API proposal is under active discussion and development and subject to change.
We're eager to hear your thoughts on the Select URL API.
- Proposal: Review the detailed proposal.
- Discussion: Join the ongoing discussion to ask questions and share your insights.
Stay Informed
- Mailing List: Subscribe to our mailing list for the latest updates and announcements related to the Select URL and Shared Storage APIs.
Need Help?
- Developer Support: Connect with other developers and get answers to your questions in the Privacy Sandbox Developer Support repository.